Trước đây blog Code Dạo toàn viết về code, hôm nay mình đổi phong cách, viết lên tầm thiết kế hệ thống cho nó máu!
Kì này, chúng ta tìm sẽ thử thiết kế một hệ thống lớn, phục vụ hàng triệu người dùng. Đây là một câu hỏi khá là khó và không có câu trả lời chính xác (Tương tự những câu: vì sao phụ nữ buồn, làm sao để con gái sướng).
Nhân hôm trước, mình có làm bài phỏng vấn tại công ty nọ, có một câu hỏi yêu cầu thiết kế một ứng dụng chat sao cho có thể scale cho hàng triệu người dùng. Trước giờ mình không có kinh nghiệm với cái này, qua quá trình tự tìm hiểu cũng đủ biết sơ sơ để chém gió.
Vì vậy mình chia sẻ cho các bạn, Mọi người cùng đọc và góp ý nhé.
Tại sao thiết kế hệ thống hàng triệu người dùng lại khó?
Thiết kế một hệ thống cho 1, 2 người dùng, chạy trên máy local vô cùng đơn giản. Tuy nhiên, một bài toán dễ đến mấy, khi scope lên đến tầm hàng triệu người dùng cũng sẽ trở thành một bài toán lớn và khó hơn rất nhiều lần.
Tại sao vậy? Lấy ví dụ nhé, một con server cùi có thể chịu được 100 request mỗi giây. Khi lượng request lên đến 10000 thì có thể nâng cấp server xịn hơn. Nhưng khi lượng request lên đến 1-2 triệu thì không thể nâng cấp server được nữa, mà phải thiết kế sao cho nhiều server chạy cùng lúc.
Hoặc đơn thuần như việc sắp xếp dữ liệu, với 1000 entry thì chạy trong nháy mắt, nhưng với chục triệu, trăm triệu entry thì có thể chạy đến hàng tiếng, lúc này ta phải lựa chọn thuật giải tối ưu. Vấn đề này liên quan tới scalability (khả năng mở rộng của hệ thống).
Mặt khác, không phải developer nào cũng có cơ hội để giải quyết bài toán scalability. Với các ứng dụng nhỏ,những dự án outsource, ít người đôi khi chỉ cần dùng một số giải pháp đơn giản là đã giải quyết được.
Ở các công ty lớn như Facebook, Uber, Google, đây là bài toán bắt buốc phải giải. Ở Việt Nam chắc sẽ có một số công ty lớn như Zalo, Lozi, Tiki, Thế Giới Di Động mới cần giải quyết vấn đề scalability này.
Ba yếu tố quan trọng của một hệ thống
- Performance: Tốc độ phản hồi của hệ thống, được đo bằng đơn vị thời gian, có thể là giây hoặc mili giây. Hệ thống hoạt động càng nhanh thì người dùng làm được nhiều việc hơn, đem lại lợi nhuận cao hơn. Hệ thống mà quá chậm thì sẽ không có ai sử dụng.
- Availability: Chỉ khả năng hoạt động của hệ thống vào mọi thời điểm, được đo bằng uptime. Ví dụ như trong 100 ngày, hệ thống hoạt động 99 ngày còn 1 ngày die thì uptime là 99/100 = 99%.
- Uptime của các hệ thống lớn như Facebook, Google, Uber phải luôn >99%, vì chỉ cần ngưng hoạt động vài phút là các công ty sẽ thiệt hại từ vài nghìn cho tới vài triệu đô.
- Hôm trước phỏng vấn ở Agoda, anh Director chia sẻ 1 giây Agoda kiếm được 1000 đô. Bum bum bum 3s là 3000 đô. Do đó hệ thống mà sập cỡ 5-10 phút là thiệt hại sẽ… hơi bị bự.
- Scalability: Khả năng mở rộng của hệ thống. Liệu khi có đông user hơn thì hệ thống có thể mở rộng (scale) được không? Việc scale có thể thực hiện dễ dàng, nhanh chóng hay không? Chi phí scale như thế nào?
- Ví dụ lượng người dùng tăng gấp 10, ta chỉ cần tăng gấp 2 hoặc gấp 5 lượng server là phục vụ được, hệ thống có scalability cao. Tuy nhiên, nếu ta phải tăng gấp 100, gấp 200 lượng server, hoặc không thể thêm server để phục vụ chừng đó người dùng, hệ thống có scalability thấp.
Các thiết kế thường dùng cho hệ thống lớn
1. Đảm bảo perfomance
- Cân bằng tải với Load Balancer: Load Balancer là một thiết bị (phần cứng hoặc phần mềm) cho phép cân bằng tải đến nhiều server.
- Giả sử ta có 1 server có thể phục vụ 1000 người. Để phục vụ 10000 người, ta có thể chạy 10 server. Người dùng sẽ không trực tiếp truy cập tới server, mà chỉ truy cập tới load balancer. LB sẽ điều tiết, cân bằng lượng tải trên 10 server này.
- Phân tán dữ liệu với Content Delivery Network (CDN): CDN là một mạng lưới các server được phân bố trên nhiều khu vực.
- Giả sử server thiend*a để ở Mĩ, mỗi khi bạn truy cập đến thiend*a, bạn sẽ phải kết nối tới server bên Mĩ, khá là chậm. Với CDN, dữ liệu sẽ được để ở Mĩ, Việt Nam, Lào, Cam.
- Khi bạn kết nối đến thiend*a, bạn sẽ kết nối tới server gần nhất (Việt Nam), tốc độ sẽ nhanh hơn nhiều. CDN thường chỉ chứa các dữ liệu tĩnh, ít thay đổi như media (video, ảnh, nhạc), file (CSS, JS)
- Caching: Cache là một kĩ thuật để tăng tốc độ đọc dữ liệu, bằng cách lưu dữ liệu sẵn vào cache server để tăng tốc độ đọc ở những lần sau.
- Ví dụ mỗi lần gọi API hoặc Database mất 10s, ta chỉ gọi lần đầu và lưu kết quả vào cache. Ở những lần sau, đọc kết quả từ cache chỉ mất 0.5s thôi. Trong series Nhận diện Idol, mình có sử dụng Redis làm Cache Server.
2. Đảm bảo Availability
- Master/Slave: Thay vì chỉ chạy 1 server, ta chạy 2 hoặc nhiều hơn. 1 server chính gọi là master, các server còn lại là slave. Khi master có vấn đề (sập nguồn hay crash), một slave sẽ được chỉ định để lên thay thế master.
- Replication: Thường được kết hợp chung với Load Balancer. Code của ứng dụng sẽ được deploy lên nhiều server. Khi có 1 server die, load balancer sẽ chuyển request sang server khác, đảm bảo request vẫn được thực hiện.
3. Đảm bảo scalability
- Vertical Scaling: Tăng sức mạnh phần cứng cho server bằng cách… lắm thêm RAM, lắm thêm chip, thay ổ cứng bằng ổ SSD.
- Cách này khá nhanh và dễ thực hiện (Nếu dùng AWS hoặc Azure chỉ việc đổi config là xong, 30 giây). Nhược điểm của cách làm này là: phần cứng luôn có một giới hạn, phần cứng càng mạnh thì giá càng… trên trời nên chỉ có thể mở rộng đến một giới hạn nhất định
- Horizontal Scaling: Thay vì tăng sức mạnh cho 1 server, ta thêm nhiều server vào hệ thống và chạy cùng lúc.
- Cách này làm cho thiết kế phức tạp hơn, nhưng bù lại ta có thể dùng phần cứng rẻ tiền lắp thêm vào, khả năng mở rộng hầu như không giới hạn (Google File System sử dụng hướng này, data center hàng nghìn con server linux loại rẻ).
- Với application server (server chứa code): Cần đảm bảo code ở server là stateless (không lưu trạng thái). Ta chỉ cần chạy vài chục đến vài trăm server (server farm), các server này đều ẩn sau load balancer. Load balancer sẽ điều tiết tải lên các server này. Nếu lượng người dùng tăng, chỉ việc thêm server là xong.
- Với database server: Với database thì mọi chuyện phức tạp hơn nhiều, ta không thể chạy database ở nhiều máy vì dữ liệu có thể sẽ bị bất đồng bộ.
- Cho các ứng dụng thuần read/ít write (blog, trang tin tức,…) ta có thể dùng thiết kế master/slave : 1 server master để read/write, nhiều server slave chỉ để đọc. Khi người dùng cần đọc thông tin từ database, ta có thể đọc từ master hoặc một trong các slave. Khi cần ghi dữ liệu vào database, ta ghi vào master, sau đó đồng bộ dần qua các slave.
- Cho các ứng dụng có lượng read/write nhiều như nhau (Agoda, Facebook), ta có thể áp dụng cách sharding. Chia database thành các mẫu shard nhỏ (theo IP, theo địa điểm vật lý của người dùng,…). Các database NoSQL (MongoDB, Cassandra) đều hỗ trợ sharding kiểu này.
Kết
Những kĩ thuật mình nhắc đến trong bài chỉ mang tính chất “cưỡi ngựa xem hoa” (Vậy mà đã gần 2k chữ). Nói thì có vẻ đơn giản, nhưng nếu đi sâu vào một trong các kĩ thuật (load balancer, master/slave, sharding) cũng sẽ mất vài nghìn chữ. Do vậy, mình chỉ liệt kê sơ để bạn đọc có hướng để tìm hiểu thêm.
Mặc dù có thể cả đời bạn không cần dùng đến nhưng kĩ năng này (suốt ngày làm web tin tức, web bán hàng, dự án nội bộ công ty), hãy thử tìm hiểu sâu về chúng nhé! Chúng sẽ tăng cường khả năng tư duy và tầm nhìn hệ thống của bạn lên rất nhiều đấy!
Nếu có hứng thú, các bạn cứ để comment lại, mình sẽ viết nguyên 1 series về vấn đề này sau nhé!
Dưới đây là một số link để tham khảo thêm, bạn nào có tài liệu hay thì cứ share trong mục comment nhé:
- Hired in tech – System Design
- High Scalability.com
- http://tutorials.jenkov.com/software-architecture/scalable-architectures.html
- http://www.aosabook.org/en/distsys.html
- http://horicky.blogspot.co.uk/2010/10/scalable-system-design-patterns.html
- http://horicky.blogspot.co.uk/2008/02/scalable-system-design.html
- https://lethain.com/introduction-to-architecting-systems-for-scale/
P/S: Để theo dõi bài viết trên Tôi Đi Code Dạo, nhớ Subscribe Chat Bot của tụi mình nha. Bot của Code Dạo sẽ gửi bạn những bài viết cực kì hay ho về kĩ năng mềm và cứng, kinh nghiệm trong ngành vào thứ 4 hàng tuần nhé!
Like ủng hộ :D, hiếm lắm mới có một số bài viết chuyên sâu và tổng quát thế này. Thank nhé.
LikeLiked by 1 person
Thank bác nhiều nhiều, làm 1 seri về cái này đi bác ơi, tư duy về hệ thông gần như tên dev nào cũng nên biết ạ
LikeLike
Hi code dạo, bạn có thể viết 1 ebook về vấn đề này được không. Để mọi người nắm rõ hơn.
LikeLike
Hay lắm ! nhưng cần đi sâu vào ví dụ cụ thể ? Cám ơn bạn !
LikeLike
Thanks anh
LikeLike
Cám ơn em đã khai sáng cho anh!
LikeLike
Thank anh. Nếu được anh viết một serie về vấn đề này với ạ !
LikeLike
Thanks !
LikeLike
Thank bạn 🙂
LikeLike
Góp ý vài thuật ngữ giang hồ cho scale:
Vertical Scaling hay còn gọi là Scale up
Horizontal scaling gọi là Scale out
Ngoài ra còn có scale down & scale in (nhưng chắc ít ai xài, thường chỉ apply cho những system có cơ chế auto scale)
LikeLike
Rất tuyệt, bác làm seri đi sâu vào luôn đi ạ 😀
LikeLike
Bài viết mang tính chất khai phá rất hay
LikeLike
Hay quá thớt ơi, viết series đi, khai sáng cho 500 anh em
LikeLike
1 giây Agoda kiếm được 1000 đô, vị chi 1 ngày 86400 x 1000 đô = 86 triệu đô. Con số này có ảo quá ko ad. Em thì không tin lắm làm sao mà kiếm được nhiều tiền thế?
LikeLike
hay lắm
LikeLike
A cũng ko tin lắm nên về check thử. Tìm ko thấy revunue của Agoda, chỉ có của cty mẹ nó là Priceline Group.
https://en.m.wikipedia.org/wiki/The_Priceline_Group
Revenue 1 năm là 10.64 tỷ đô. Nếu tính 1 ngày chỉ họat động 8 tiếng thì đúng là vào khoảng 1000USD/giây ấy. Còn thằng Agoda là cty con chắc ít hơn nhiều :))
LikeLike
Làm cái series đi anh
LikeLike
Anh làm series đi anh 😀 😀
LikeLike
Ở VN rất ít website hứng được >1 triệu request cùng lúc. Vì dù có mở rộng bề ngang (thêm nhiều server) thì chi phí vẫn đắt đỏ. Giả sử scale lên 20 con ở aws thì tiền cũng lên mấy chục ngàn mĩ kim. Thế mà bọn Tây chúng nó tiêu tiền nhìn chóng cả mặt :3
LikeLike
Thật ra là do requirement nữa bạn. Ko phải hệ thống nào cũng lớn đến mức đấy ;). Mấy app nho nhỏ thì host server cùi, to hơn thì 2,3 con server là đủ xử ròo 😉
LikeLike
Làm series đi bạn. vote
LikeLike
Cảm ơn các bạn đã comment góp ý. Về sau có thời gian thì mình sẽ viết series riêng nhé 😉
LikeLike
Cám ơn Hoàng đã mở mang kiến thức cho mình nhé
LikeLike
Chủ đề này hay đấy bạn 😀
LikeLike
Cảm ơn Hoàng ! Bài viết của bạn rất hữu ích…Vote bạn là Series về chủ đề này.
LikeLike
Hii hay quá. cảm ơn bạn :<
LikeLike
Mong anh viết sâu hơn về vấn đề này,có cả những ví dụ trong code thì càng dễ hiểu
LikeLike
Thank Hoang da chia se, ngoi hong phan ke tiep (y)
LikeLike
Chủ đề rất hay, cảm ơn bạn đã chia sẻ, cố gắng viết tiếp phần tiếp theo cho anh em đọc nha
LikeLike
Cách đây hơn 10 năm, Võ Lâm Truyền Kỳ đã chịu được hàng triệu lượt truy cập. Mình rất tò mò cách họ làm?
LikeLike
VLTK mua source của Tàu Khựa. Gì chứ về thiết kế hệ thống thì VN phải xách dép qua Tàu học dài dài. Dân nó đông nên mấy app, web vừa ra mắt đã có lượng user từ vài trăm nghìn đến vài triệu, scale ko khéo là vỡ mật ngay :3
LikeLike
A có blog hay site nào về thiết kế hệ thống của bọn Tàu không a 😀
LikeLike
Bài viết rất hay, giúp tôi có 1 cái nhìn tổng quan. Nếu Hoàng ra sách thì mình sẽ mua về đọc để có thêm kiến thức.
LikeLike
làm cụ thể hơn đi bác
LikeLike
Bài viết rất hay, bác làm cuốn sách chi tiết về cái này + với bigdata thì tuyệt
LikeLike
Cám ơn anh. Bài viết hay. Có vài bạn muốn ví dụ cụ thể thì chỉ có thể tự học, theo kinh nghiệm của mình thì khi bạn join vào một project lớn bạn mới thấy được những thứ thú vị. Tuy nhiên mình có refer cho ai muốn tìm hiểu đó là Spring Framework, Spring Boot, Spring Cloud Ribbon (load balancer), Cache, Gateway server, Eureka Server…
LikeLike
Bữa giờ e tò mò : không biết thằng facebook nó lưu tin nhắn trong database như thế nào ta. 1 người có thể nhắn 100-1000 tin nhắn. Khi cả mấy trăm triệu người thì facebook nó query message để hiển thị tin nhắn như thế nào cho nhanh a hè. Vì trước giờ e chỉ thấy tin nhắn thì lưu trong 1 bảng thôi
LikeLike
hay quá bạn ơi 👍
LikeLike
Cảm ơn bài viết của bạn, có lẽ đây là bài viết để người code hiểu rõ hơn về hướng đi cho những dự án lớn, rất hay !
LikeLike
có khi nào con Load Balancer chết không nhỉ hay nó chịu 1 lượng request quá lớn nên không forward lại cho các server khác kịp. Nếu vậy thì có solution nào không nhỉ ?
LikeLike
thank you!
LikeLike
So we cannot use memory cache like Guava cache (Java)..for scalability because it is kind of stateful
LikeLike
Thường thì caching trong mấy hệ thống này sẽ có server cache riêng bạn 😀
LikeLike
vậy để application có thể stateless, data nên ở scope request, còn data với application hay session scope thì minh save ở cache server đúng ko bạn
LikeLike
Bài viết lâu rồi mà đọc lại vẫn hay ghê
LikeLike
Bài rất hay, rất thực tế.
Bạn thân mình đã và đang sử dụng 2 kỹ thuật là Load Banlancer và (CDN – CloudFront) của AWS. Thấy rất là hữu ích và dễ sử dụng.
LikeLike
Cơ sở dữ liệu phân tán thì sao ạ
LikeLike
Cám ơn anh
LikeLike
Bài viết từ lâu rồi nhưng đọc thấy hay và tỷ mỉ quá. Rất mang tính ứng dụng thực tế cao. Cảm ơn ad rất nhiều vì đã chia sẻ những kiến thức rất thực tế.
LikeLike